package main

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"gitee.com/czy233/go-ceph/rados"
)

type ClientHandle struct {
	cli          *http.Client
	ioctx        *rados.IOContext
	conn         *rados.Conn
	needCheckAll int
}

// 从区块链返回的区块信息
type ObjIDsInBlock struct {
	BlockID uint64   `json:"blockid"` // 区块ID
	IDs     []string `json:"ids"`     // 区块内包含的对象ID
}

type objReocver struct { // using when perpare recover objs
	Oid  string
	From int8
}

// var checkCount [4]int // 4家机构被抽查次数

// init
func (c *ClientHandle) Init() {
	var err error
	c.conn, err = rados.NewConn()
	if err != nil {
		fmt.Println("cluster", *ClusterID, ":", "error when invoke a new connection:", err)
		return
	}

	err = c.conn.ReadConfigFile(CephConfigPath)
	if err != nil {
		fmt.Println("cluster", *ClusterID, ":", "error when read default config file:", err)
		return
	}

	err = c.conn.Connect()
	if err != nil {
		fmt.Println("cluster", *ClusterID, ":", "error when connect:", err)
		return
	}

	fmt.Printf("cluster [%d] connect ceph cluster ok!", *ClusterID)

	c.ioctx, err = c.conn.OpenIOContext(CephPoolName)
	if err != nil {
		fmt.Println("cluster", *ClusterID, ":", "error when open IO context:", err)
		return
	}
	c.cli = &http.Client{}
	c.needCheckAll = -1
}

func (c *ClientHandle) Shutdown() {
	c.conn.Shutdown()
}

// 从待恢复channel获取需要数据恢复的对象ID和所属区块高度
func (c *ClientHandle) doRecovery(recoverChan *chan Recover) {
	for { // 从待恢复队列取出待恢复对象
		objRecovering := <-*recoverChan
		curTime := time.Now()
		fmt.Printf("cluster [%d] -> start_recovering_obj: %s, time: %v, timestamp: %v\n", *ClusterID, objRecovering.ObjId, curTime.Format("2006-01-02 15:04:05.000"), curTime.UnixMilli())
		// 起一个协程恢复该对象
		go c.recoverObj(objRecovering) // 使用纠删码恢复该对象
		fmt.Printf("cluster [%d] -> end recover: %s\n", *ClusterID, objRecovering.ObjId)
	}
}

// 恢复指定的对象
func (c *ClientHandle) recoverObj(objRecovering Recover) {
	if objRecovering.ObjId[0:2] != "@@" { // 非冗余校验块的恢复, 寻找该obj在纠删码条带中的位置
		// 从Fabric获取待恢复对象所在的区块信息
		// objIDsInBlock, err := c.GetBlockFromFabric(StrHashToNum(strconv.Itoa(*ClusterID)+"_"+objRecovering.ObjId, ChannelCount), objRecovering.BlockHeight)
		objIDsInBlock, err := c.GetBlockFromFabric(objRecovering.BlockHeight)
		if err != nil {
			fmt.Printf("cluster [%d] -> Get block from fabric error: %s\n", *ClusterID, err)
			recoveringObjsMap.Delete(objRecovering.ObjId) // rm flag of obj recovering
			return
		}
		var QuinID, ObjID int // 待恢复块所在纠删码条带的位置
		var block Block
		block.Parse(objIDsInBlock)                   // 解析区块信息
		blockDistribute := GenerateDataLayout(block) // 生成该区块的数据布局
		// prepare recover objs
		for i := 0; i < len(blockDistribute.Quin); i++ {
			for j := 0; j < len(blockDistribute.Quin[i].Objs); j++ {
				if objRecovering.ObjId == string(blockDistribute.Quin[i].Objs[j].Oid) {
					QuinID = i
					ObjID = j
					goto dealQuin // 找到该obj所在的位置
				}
			}
		}
		fmt.Printf("cluster [%d] -> find location in 5-group for recover obj error\n", *ClusterID)
		return

	dealQuin:
		// fmt.Printf("cluster [%d] -> write recovery data beginning, ", *ClusterID)
		// fmt.Println("Timestamp: ", time.Now().Unix())
		quin := &blockDistribute.Quin[QuinID] // 指向待恢复块所在纠删码条带中的位置
		if len(quin.Objs) == 3 { // 纠删码条带中有三个objs
			if len(quin.Storage) == 1 { // 来自三家不同机构 -> 3 + 1
				objData := make([][]byte, 4)
				for i := 0; i < 3; i++ { // 读取恢复所需数据块
					if i == ObjID { // 待恢复块, 填入冗余校验块
						objData[i] = make([]byte, ObjectSize)
					} else { // 其他块正常读取
						objData[i], err = c.GetObjFromClusterTLS(string(quin.Objs[i].Oid), int(quin.Objs[i].From)) // 基于HTTP获取相关数据对象
						if err != nil {
							fmt.Printf("cluster [%d] -> get data from cluster [%d] while recover error: %s\n", *ClusterID, quin.Objs[i].From, err)
							return
						}
						// err = c.checkData(string(quin.Objs[i].Oid), int(quin.Objs[i].From), objData[i]) //验证恢复错误块所需数据的完整性
						// if err != nil {
						// 	fmt.Println(err.Error())
						// 	return
						// } else {
						// 	fmt.Printf("cluster [%d] -> check origin data correct, length: %d\n", *ClusterID, len(objData[i]))
						// }
					}
				}
				redundancyObjId := "@@" + strconv.Itoa(int(quin.Objs[0].From)) + "_" + string(quin.Objs[0].Oid) + "@" + strconv.Itoa(int(quin.Objs[1].From)) + "_" + string(quin.Objs[1].Oid) + "@" + strconv.Itoa(int(quin.Objs[2].From)) + "_" + string(quin.Objs[2].Oid)
				objData[3], err = c.GetObjFromClusterTLS(redundancyObjId, int(quin.Storage[0])) // 读取纠删码校验块
				if err != nil {
					fmt.Printf("cluster [%d] -> get data from cluster [%d] while recover error: %s\n", *ClusterID, quin.Storage[0], err)
					return
				}
				// err = c.checkData(redundancyObjId, int(quin.Storage[0]), objData[3]) //验证纠删码校验块的完整性
				// if err != nil {
				// 	fmt.Println(err.Error())
				// 	return
				// } else {
				// 	fmt.Printf("cluster [%d] -> check origin data correct, length: %d\n", *ClusterID, len(objData[3]))
				// }

				err = ErasureReconst(3, 1, objData, []int{ObjID}) // 使用纠删码恢复数据
				if err != nil {
					fmt.Printf("cluster [%d] -> erasure encode error while recovering unrendundancy obj: %s\n", *ClusterID, err)
					return
				}
				// err = ErasureEncodeXor(3, 1, objData) // 使用Xor计算3+1纠删码
				// if err != nil {
				// 	fmt.Println(err.Error())
				// 	return
				// }
				// err = c.checkData(objRecovering.ObjId, *ClusterID, objData[ObjID]) //验证恢复块数据完整性
				// if err != nil {
				// 	fmt.Println(err.Error())
				// 	return
				// } else {
				// 	fmt.Printf("cluster [%d] -> check recovery data correct, length: %d\n", *ClusterID, len(objData[ObjID]))
				// }
				err = c.WriteDataToLocalCephWithoutHash(objRecovering.ObjId, objData[ObjID]) // 写入恢复块到本地Ceph
				if err != nil {
					fmt.Println(err.Error())
					return
				}
			} else if len(quin.Storage) == 2 { // 来自两家不同机构, 其中第一个和第二个来自同一家机构 -> 两个 2 + 1
				objData := make([][]byte, 3)
				var redundancyObjLoc, redundancyObjSeq int
				var redundancyObjId string
				if ObjID == 2 { // 待恢复块为第三个, 使用第一个块恢复
					redundancyObjLoc = 1
					redundancyObjSeq = 0
					redundancyObjId = "@@" + strconv.Itoa(int(quin.Objs[0].From)) + "_" + string(quin.Objs[0].Oid) + "@" + strconv.Itoa(int(quin.Objs[2].From)) + "_" + string(quin.Objs[2].Oid)
					objData[0], err = c.GetObjFromClusterTLS(string(quin.Objs[0].Oid), int(quin.Objs[0].From)) // 读取原始数据块
					if err != nil {
						fmt.Printf("cluster [%d] -> get data from cluster [%d] while recover error: %s\n", *ClusterID, quin.Objs[0].From, err)
						return
					}
					objData[1] = make([]byte, ObjectSize)
				} else { // 待恢复块为第一/二个, 需要使用第三个块恢复
					redundancyObjLoc = 0
					redundancyObjSeq = ObjID
					redundancyObjId = "@@" + strconv.Itoa(int(quin.Objs[ObjID].From)) + "_" + string(quin.Objs[ObjID].Oid) + "@" + strconv.Itoa(int(quin.Objs[2].From)) + "_" + string(quin.Objs[2].Oid)
					objData[1], err = c.GetObjFromClusterTLS(string(quin.Objs[2].Oid), int(quin.Objs[2].From)) // 读取原始数据块
					if err != nil {
						fmt.Printf("cluster [%d] -> get data from cluster [%d] while recover error: %s\n", *ClusterID, quin.Objs[2].From, err)
						return
					}
					objData[0] = make([]byte, ObjectSize)
				}

				objData[2], err = c.GetObjFromClusterTLS(redundancyObjId, int(quin.Storage[redundancyObjSeq])) // 读取冗余校验块
				if err != nil {
					fmt.Printf("cluster [%d] -> get data from cluster [%d] while recover error: %s\n", *ClusterID, quin.Storage[redundancyObjSeq], err)
					return
				}
				// objData[2] = make([]byte, len(objData[0]))
				err = ErasureReconst(2, 1, objData, []int{redundancyObjLoc}) // 使用纠删码计算待恢复块
				// err = ErasureEncodeXor(2, 1, objData) // 使用Xor计算2+1纠删码
				if err != nil {
					fmt.Printf("cluster [%d] -> erasure encode error while recovering unrendundancy obj: %s\n", *ClusterID, err)
					return
				}
				// err = c.checkData(objRecovering.ObjId, int(quin.Objs[ObjID].From), objData[redundancyObjLoc]) //验证恢复块数据完整性
				// if err != nil {
				// 	fmt.Println(err.Error())
				// 	return
				// } else {
				// 	fmt.Printf("cluster [%d] -> check recovery data correct, length: %d\n", *ClusterID, len(objData[redundancyObjLoc]))
				// }
				err = c.WriteDataToLocalCephWithoutHash(objRecovering.ObjId, objData[redundancyObjLoc]) // 写入恢复块到本地Ceph
				if err != nil {
					fmt.Println(err.Error())
					return
				}
				fmt.Printf("cluster [%d] -> write recovery data to ceph correct\n", *ClusterID)
			} else if len(quin.Storage) == 3 { // 均来自一家机构 -> 三个 1 + 1
				redundancyObjId := "@@" + strconv.Itoa(int(quin.Objs[ObjID].From)) + "_" + string(quin.Objs[ObjID].Oid)
				objData, err := c.GetObjFromClusterTLS(redundancyObjId, int(quin.Storage[ObjID])) // 读取原始数据块
				if err != nil {
					fmt.Printf("cluster [%d] -> get data from cluster [%d] while recover error: %s\n", *ClusterID, quin.Objs[ObjID].From, err)
					return
				}
				// err = c.checkData(objRecovering.ObjId, int(quin.Objs[ObjID].From), objData) //验证恢复块数据完整性
				// if err != nil {
				// 	fmt.Println(err.Error())
				// 	return
				// } else {
				// 	fmt.Printf("cluster [%d] -> check recovery data correct, length: %d\n", *ClusterID, len(objData))
				// }
				err = c.WriteDataToLocalCephWithoutHash(objRecovering.ObjId, objData) // 写入恢复块到本地Ceph
				if err != nil {
					fmt.Println(err.Error())
					return
				}
				fmt.Printf("cluster [%d] -> write recovery data to ceph correct\n", *ClusterID)
			} else { // 5-group 错误
				fmt.Printf("cluster [%d] -> error 5-group when recover unrendundancy obj: length of storage array error\n", *ClusterID)
				return
			}
		} else if len(quin.Objs) == 1 { // 同一区块内剩余对象数小于3时，生成的五元组中对象数为1，使用 1 + 1 容错
			if len(quin.Storage) == 1 {
				redundancyObjId := "@@" + strconv.Itoa(int(quin.Objs[ObjID].From)) + "_" + string(quin.Objs[ObjID].Oid)
				objData, err := c.GetObjFromClusterTLS(redundancyObjId, int(quin.Storage[ObjID])) // 读取原始数据块
				if err != nil {
					fmt.Printf("cluster [%d] -> get data from cluster [%d] while recover error: %s\n", *ClusterID, quin.Objs[ObjID].From, err)
					return
				}
				// err = c.checkData(objRecovering.ObjId, int(quin.Objs[ObjID].From), objData) //验证恢复块数据完整性
				// if err != nil {
				// 	fmt.Println(err.Error())
				// 	return
				// } else {
				// 	fmt.Printf("cluster [%d] -> check recovery data correct, length: %d\n", *ClusterID, len(objData))
				// }
				err = c.WriteDataToLocalCephWithoutHash(objRecovering.ObjId, objData) // 写入恢复块到本地Ceph
				if err != nil {
					fmt.Println(err.Error())
					return
				}
				fmt.Printf("cluster [%d] -> write recovery data to ceph correct\n", *ClusterID)
			} else { // 5-group 错误
				fmt.Printf("cluster [%d] -> error 5-group when recover unrendundancy obj: length of storage array error\n", *ClusterID)
				return
			}
		} else { // 5-group 错误
			fmt.Printf("cluster [%d] -> error 5-group when recover unrendundancy obj: length of objs array error\n", *ClusterID)
			return
		}
		// duration := time.Since(startTime).Milliseconds()
		// throughput := float64(ObjectSize) / float64(duration) * 1000 / 1024 / 1024
		// fmt.Printf("cluster [%d] -> recover obj [%s] duration: %d ms, throughput: %f MB/s\n", *ClusterID, objRecovering.ObjId, duration, throughput)
	} else { // 冗余校验块的恢复
		var err error
		objIdArray := strings.Split(objRecovering.ObjId[2:], "@") // 拆解数据对象ID
		objData := make([][]byte, len(objIdArray)+1)
		for i := 0; i < len(objIdArray); i++ {
			from, _ := strconv.Atoi(string(objIdArray[i][0]))
			objData[i], err = c.GetObjFromClusterTLS(string(objIdArray[i][2:]), from) // 基于HTTP获取相关数据对象
			if err != nil {
				fmt.Printf("cluster [%d] -> get data from cluster [%d] while recover error: %s\n", *ClusterID, from, err)
				return
			}
		}
		// err = ErasureEncodeXor(len(objIdArray), 1, objData) // 使用异或计算待恢复块
		objData[len(objIdArray)] = make([]byte, len(objData[0]))
		err = ErasureReconst(len(objIdArray), 1, objData, []int{len(objIdArray)}) // 使用纠删码恢复数据
		if err != nil {
			fmt.Printf("cluster [%d] -> erasure encode error while recovering unrendundancy obj: %s\n", *ClusterID, err)
			return
		}
		// err = c.checkData(objRecovering.ObjId, *ClusterID, objData[len(objIdArray)]) //验证恢复块数据完整性
		// if err != nil {
		// 	fmt.Println(err.Error())
		// 	return
		// } else {
		// 	fmt.Printf("cluster [%d] -> check recovery data correct, length: %d\n", *ClusterID, len(objData[len(objIdArray)]))
		// }
		err = c.WriteDataToLocalCephWithoutHash(objRecovering.ObjId, objData[len(objIdArray)]) // 写入恢复块到本地Ceph
		if err != nil {
			fmt.Println(err.Error())
			return
		}
	}
	recoveringObjsMap.Delete(objRecovering.ObjId) // rm flag of obj recovering
	curTime := time.Now()
	fmt.Printf("cluster [%d] -> write_recovered_obj_to_ceph, time: %v, timestamp: %v\n", *ClusterID, curTime.Format("2006-01-02 15:04:05.000"), curTime.UnixMilli())
}

// 数据抽查loop，每隔一段时间抽查一次
func (c *ClientHandle) doCheck() {
	for {
		if c.needCheckAll != -1 {
			c.checkAll()
		} else {
			time.Sleep(5 * time.Second)
			c.checkRandom()
		}
	}
}

func (c *ClientHandle) checkAll() {
	checkHeight := HeightStartCheck
	for {
		objIDsInBlock, err := c.GetBlockFromFabric(uint64(checkHeight))
		if err != nil {
			fmt.Printf("cluster [%d] -> Get block from fabric error: %s\n", *ClusterID, err)
			break
		}
		if objIDsInBlock.BlockID < HeightStartCheck { // 区块高度小于设定的开始抽查高度
			fmt.Printf("cluster [%d] -> Block height lower than height start checking\n", *ClusterID)
			break
		}
		var block Block
		if block.ParseWithParityBlock(objIDsInBlock) == false { // 解析区块到block
			fmt.Printf("cluster [%d] -> block [%d] is empty, do not check\n", *ClusterID, block.BlockId)
		} else {
			c.checkBlockOfCluster(&block)
		}
		checkHeight++
	}
	c.needCheckAll = -1
}

func (c *ClientHandle) checkRandom() {
	rand.Seed(time.Now().Unix())
	objIDsInBlock, err := c.GetRandomBlockFromFabric() // 基于HTTP从Fabric系统获取一个链上的随机区块
	if err != nil {
		fmt.Printf("cluster [%d] -> Get block from fabric error: %s\n", *ClusterID, err)
		return
	}
	if objIDsInBlock.BlockID < HeightStartCheck { // 区块高度小于设定的开始抽查高度
		fmt.Printf("cluster [%d] -> Block height lower than height start checking\n", *ClusterID)
		return
	}
	if objIDsInBlock.BlockID > BlockHeightRedundunt { // 区块高度大于冗余校验块的高度
		fmt.Printf("cluster [%d] -> Block height higher than height end redundunt\n", *ClusterID)
		return
	}
	var block Block
	if block.ParseWithParityBlock(objIDsInBlock) == false { // 解析区块到block
		fmt.Printf("cluster [%d] -> block [%d] is empty, do not check\n", *ClusterID, block.BlockId)
		return
	}
	c.checkBlock(&block)
}

// 随机抽查某区块中的所有对象中的某4KB数据完整性
func (c *ClientHandle) checkBlock(block *Block) {
	for _, obj := range block.Objs { // 循环抽查区块内的所有对象
		data4Koffset := rand.Intn(1024)
		// checkCount[obj.From]++
		// dataProof, err := c.GetDataWithProofFromClusterTLS(string(obj.Oid), int(obj.From), rand.Intn(1024))
		dataProof, err := c.GetDataWithProofFromClusterTLS(string(obj.Oid), int(obj.From), data4Koffset, block.BlockId) // 基于HTTP向被抽查机构请求被抽查的数据及其Merkle证明
		if err != nil {
			fmt.Printf("cluster [%d] -> block [%d]: get data&proof of %s from [%d] error: %s\n", *ClusterID, block.BlockId, string(obj.Oid), int(obj.From), err)
			c.needCheckAll = int(obj.From)
			continue
		}
		if !checkDataWithMerkleProof(dataProof, data4Koffset) { // 验证Merkle证明的有效性
			continue
		}
		hashFromFabric, err := c.GetHashFromFabric(string(obj.Oid), int(obj.From)) // 从Fabric获取被抽查对象的链上哈希
		if err != nil {
			fmt.Printf("cluster [%d] -> block [%d]: get data from fabric error: %s\n", *ClusterID, block.BlockId, err)
			continue
		}
		if !bytes.Equal(dataProof.Proof.Proof[len(dataProof.Proof.Proof)-1][0], hashFromFabric) { // 判断抽查数据Merkle根哈希的有效性
			fmt.Printf("cluster [%d] -> block [%d]: failed to check merkle proof with hash from fabric\n", *ClusterID, block.BlockId)
			continue
		}
		fmt.Printf("cluster [%d] -> block [%d]: check obj: [%d_%s](offset=%d) Correct\n", *ClusterID, block.BlockId, int(obj.From), string(obj.Oid), data4Koffset)
	}
}

// 随机抽查某区块中的特定机构对象中的某4KB数据完整性
func (c *ClientHandle) checkBlockOfCluster(block *Block) {
	for _, obj := range block.Objs { // 循环抽查区块内的所有对象
		if int(obj.From) != c.needCheckAll {
			continue
		}
		data4Koffset := rand.Intn(1024)
		// checkCount[obj.From]++
		// dataProof, err := c.GetDataWithProofFromClusterTLS(string(obj.Oid), int(obj.From), rand.Intn(1024))
		dataProof, err := c.GetDataWithProofFromClusterTLS(string(obj.Oid), int(obj.From), data4Koffset, block.BlockId) // 基于HTTP向被抽查机构请求被抽查的数据及其Merkle证明
		if err != nil {
			fmt.Printf("cluster [%d] -> block [%d]: get data&proof of %s from [%d] error: %s\n", *ClusterID, block.BlockId, string(obj.Oid), int(obj.From), err)
			// c.needCheckAll = int(obj.From)
			continue
		}
		if !checkDataWithMerkleProof(dataProof, data4Koffset) { // 验证Merkle证明的有效性
			continue
		}
		hashFromFabric, err := c.GetHashFromFabric(string(obj.Oid), int(obj.From)) // 从Fabric获取被抽查对象的链上哈希
		if err != nil {
			fmt.Printf("cluster [%d] -> block [%d]: get data from fabric error: %s\n", *ClusterID, block.BlockId, err)
			continue
		}
		if !bytes.Equal(dataProof.Proof.Proof[len(dataProof.Proof.Proof)-1][0], hashFromFabric) { // 判断抽查数据Merkle根哈希的有效性
			fmt.Printf("cluster [%d] -> block [%d]: failed to check merkle proof with hash from fabric\n", *ClusterID, block.BlockId)
			continue
		}
		fmt.Printf("cluster [%d] -> block [%d]: check obj: [%d_%s](offset=%d) Correct\n", *ClusterID, block.BlockId, int(obj.From), string(obj.Oid), data4Koffset)
	}
}

func (c *ClientHandle) checkData(obj_id string, from int, data []byte) error {
	var dataHash []byte
	var wg sync.WaitGroup

	if len(data) != 4*1024*1024 { // 对于非4MB对象，直接计算sha256
		wg.Add(1)
		go func(data []byte) {
			defer wg.Done()
			// h := sha256.New()
			// h.Write(data)
			// dataHash = h.Sum(nil)
			dataHash32 := sha256.Sum256(data)
			dataHash = append(dataHash, dataHash32[:]...)
		}(data)
	} else { // 对于4MB对象，使用Merkle tree
		wg.Add(1)
		go func(data []byte) {
			defer wg.Done()
			var m MerkleTree
			m.InitConfig([]int{1, 32, 32})
			err := m.GenerateTree(data)
			if err != nil {
				fmt.Printf("cluster [%d] -> generate mktree error: %s\n", *ClusterID, err)
				return
			}
			dataHash = m.node[0][0]
		}(data)
	}
	fabricHash, err := c.GetHashFromFabric(obj_id, from) // hash from fabric
	if err != nil {
		return fmt.Errorf("cluster [%d] -> get data: %s hash from fabric error: %s\n", *ClusterID, obj_id, err)
	}
	wg.Wait()
	if !bytes.Equal(dataHash, fabricHash) {
		return fmt.Errorf("cluster [%d] -> data: %s from [%d] not pass check\nreal hash: %v\nfabric hash: %v \n", *ClusterID, obj_id, from, dataHash, fabricHash)
	}
	return nil
}

// 基于HTTP TLS从其他机构获得恢复数据
func (c *ClientHandle) GetObjFromClusterTLS(obj_id string, from int) ([]byte, error) {
	url := PrefixDealObj(from) + "get?oid=" + obj_id
	resp, err := c.cli.Get(url)
	if err != nil {
		fmt.Printf("cluster [%d] -> get obj from cluster [%d] error: %s\n", *ClusterID, from, err)
		return nil, err
	}
	defer resp.Body.Close()
	ret, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if len(ret) != ObjectSize {
		return nil, fmt.Errorf("cluster [%d] -> obj read len is not equal to ObjectSize, expect: %d, get: %d", *ClusterID, ObjectSize, len(ret))
	}
	err = c.checkData(obj_id, from, ret) // 验证数据完整性
	if err != nil {
		return ret, err
	}
	return ret, nil
}

// 基于HTTP TLS向其他机构写入校验数据
func (c *ClientHandle) PutObjToClusterTLS(obj_id string, to int, content []byte) error {
	url := PrefixDealObj(to) + "put"
	// fmt.Println(url)
	obj := ObjectBlock{
		Obj_id:  obj_id,
		Content: content,
	}
	obj_json, err := json.Marshal(obj)
	if err != nil {
		return err
	}

	resp, err := c.cli.Post(url, "application/json", bytes.NewBuffer(obj_json))
	if err != nil {
		return err
	}
	resp_body, _ := io.ReadAll(resp.Body)
	fmt.Printf("cluster [%d] -> write obj: %s to cluster [%d], length: %d, resp: %s\n", *ClusterID, obj_id, to, len(obj.Content), string(resp_body))
	return nil
}

// 基于HTTP TLS向其他机构请求抽查的数据及其proof
func (c *ClientHandle) GetDataWithProofFromClusterTLS(obj_id string, from int, offset int, blockId uint64) (*DataWithProof, error) {
	url := PrefixDealObj(from) + "check?oid=" + obj_id + "&off=" + strconv.Itoa(offset) + "&height=" + strconv.FormatUint(blockId, 10)
	// fmt.Println("cluster", *ClusterID, ":", url)
	resp, err := c.cli.Get(url)
	if err != nil {
		fmt.Printf("cluster [%d] -> get check request to cluster [%d] error: %s\n", *ClusterID, from, err)
		return nil, err
	}
	defer resp.Body.Close()
	ret, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if len(ret) == 0 {
		return nil, fmt.Errorf("cluster [%d] -> length of data&proof getted from [%d] is 0", *ClusterID, from)
	}
	dataWithProof := &DataWithProof{}
	err = json.Unmarshal(ret, dataWithProof)
	if err != nil {
		return nil, fmt.Errorf("cluster [%d] -> unmarshal data&proof from [%d] error", *ClusterID, from)
	}
	return dataWithProof, nil
}

// 基于HTTP向本机构fabric-sdk请求保存在Fabric中的对象哈希，机构内不需要TLS
func (c *ClientHandle) GetHashFromFabric(obj_id string, obj_from int) ([]byte, error) {
	url := "http://" + GetFabricSDKIPPort() + "/get?ID=" + strconv.Itoa(obj_from) + "_" + obj_id
	// fmt.Println("cluster", *ClusterID, ":", url)
	resp, err := c.cli.Get(url)
	defer resp.Body.Close()
	ret, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return ret, nil
}

// get current block height though fabric-sdk
func (c *ClientHandle) getCurrentBlockHeight() (uint64, error) {
	url := "http://" + GetFabricSDKIPPort() + "/getHeight"
	// fmt.Println("cluster", *ClusterID, ":", url)
	resp, err := c.cli.Get(url)
	defer resp.Body.Close()
	ret, err := io.ReadAll(resp.Body)
	if err != nil {
		return 0, err
	}
	currentHeight := binary.BigEndian.Uint64(ret)
	return currentHeight, nil
}

// 基于HTTP向本机构fabric-sdk请求指定channel和高度的区块，机构内不需要TLS
func (c *ClientHandle) GetBlockFromFabric(height uint64) (*ObjIDsInBlock, error) {
	url := "http://" + GetFabricSDKIPPort() + "/getBlock?height=" + strconv.FormatUint(height, 10)
	// fmt.Println("cluster", *ClusterID, ":", url)
	resp, err := c.cli.Get(url)
	defer resp.Body.Close()
	ret, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	objIDsInBlock := &ObjIDsInBlock{}
	err = json.Unmarshal(ret, objIDsInBlock)
	if err != nil {
		return nil, err
	}
	return objIDsInBlock, nil
}

// 基于HTTP向本机构fabric-sdk请求起始高度到最新高度的随机区块，机构内不需要TLS
func (c *ClientHandle) GetRandomBlockFromFabric() (*ObjIDsInBlock, error) {
	url := "http://" + GetFabricSDKIPPort() + "/getRandomBlock?start=" + strconv.Itoa(HeightStartCheck)
	// fmt.Println("cluster", *ClusterID, ":", url)
	resp, err := c.cli.Get(url)
	defer resp.Body.Close()
	ret, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	objIDsInBlock := &ObjIDsInBlock{}
	err = json.Unmarshal(ret, objIDsInBlock)
	if err != nil {
		return nil, err
	}
	return objIDsInBlock, nil
}

// 写本地数据 不上链
func (c *ClientHandle) WriteDataToLocalCephWithoutHash(oid string, data []byte) error {
	err := c.ioctx.WriteFull(oid, data)
	if err != nil {
		return fmt.Errorf("cluster [%d] -> write data to local ceph error: %s\n", *ClusterID, err)
	}
	return nil
}
